home *** CD-ROM | disk | FTP | other *** search
/ Network PC / Network PC.iso / amiga utilities / communication / internet / amitcp3.0b / src.lha / src / amitcp / api / res_send.c < prev    next >
Encoding:
C/C++ Source or Header  |  1996-09-08  |  11.5 KB  |  431 lines

  1. /*
  2.  * Copyright (c) 1985, 1989 Regents of the University of California.
  3.  * All rights reserved.
  4.  *
  5.  * Redistribution and use in source and binary forms, with or without
  6.  * modification, are permitted provided that the following conditions
  7.  * are met:
  8.  * 1. Redistributions of source code must retain the above copyright
  9.  *    notice, this list of conditions and the following disclaimer.
  10.  * 2. Redistributions in binary form must reproduce the above copyright
  11.  *    notice, this list of conditions and the following disclaimer in the
  12.  *    documentation and/or other materials provided with the distribution.
  13.  * 3. All advertising materials mentioning features or use of this software
  14.  *    must display the following acknowledgement:
  15.  *    This product includes software developed by the University of
  16.  *    California, Berkeley and its contributors.
  17.  * 4. Neither the name of the University nor the names of its contributors
  18.  *    may be used to endorse or promote products derived from this software
  19.  *    without specific prior written permission.
  20.  *
  21.  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
  22.  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
  23.  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
  24.  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
  25.  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
  26.  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
  27.  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
  28.  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
  29.  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
  30.  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
  31.  
  32.  */
  33.  
  34. #if defined(LIBC_SCCS) && !defined(lint)
  35. static char sccsid[] = "@(#)res_send.c    6.27 (Berkeley) 2/24/91";
  36. #endif /* LIBC_SCCS and not lint */
  37.  
  38. /*
  39.  * Send query to name server and wait for reply.
  40.  */
  41.  
  42. #include <conf.h>
  43.  
  44. #include <sys/param.h>
  45. #include <sys/systm.h>
  46. #include <sys/malloc.h>
  47. #include <sys/time.h>
  48. #include <sys/socket.h>
  49. #include <netinet/in.h>
  50. #include <sys/errno.h>
  51.  
  52. #include <api/arpa_nameser.h>
  53. #include <api/resolv.h>
  54. #include <kern/amiga_includes.h>
  55. #include <api/apicalls.h>
  56. #include <api/amiga_api.h>
  57. #include <kern/amiga_subr.h>     
  58. #include <kern/amiga_netdb.h>     
  59.  
  60. #ifndef AMITCP /* AmiTCP has this in the SocketBase */     
  61. static int res_sock = -1;    /* socket used for communications */
  62. #endif
  63.  
  64. /* constant */
  65. static const struct sockaddr no_addr = { sizeof(struct sockaddr), AF_INET, 0 };
  66.  
  67. #ifndef FD_SET
  68. #define    NFDBITS        32
  69. #define    FD_SETSIZE    32
  70. #define    FD_SET(n, p)    ((p)->fds_bits[(n)/NFDBITS] |= (1 << ((n) % NFDBITS)))
  71. #define    FD_CLR(n, p)    ((p)->fds_bits[(n)/NFDBITS] &= ~(1 << ((n) % NFDBITS)))
  72. #define    FD_ISSET(n, p)    ((p)->fds_bits[(n)/NFDBITS] & (1 << ((n) % NFDBITS)))
  73. #define FD_ZERO(p)    bzero((char *)(p), sizeof(*(p)))
  74. #endif
  75.  
  76. int
  77. res_send(struct SocketBase *    libPtr,
  78.      const char *        buf,
  79.      int            buflen,
  80.      char *            answer,
  81.      int             anslen)
  82. {
  83.     register int n;
  84.     int try, v_circuit, resplen, nscount;
  85.     int gotsomewhere = 0, connected = 0;
  86.     int connreset = 0;
  87.     u_short id, len;
  88.     char *cp;
  89.     fd_set dsmask;
  90.     struct timeval timeout;
  91.     struct NameserventNode *ns;
  92.     struct sockaddr_in host;
  93.     HEADER *hp = (HEADER *) buf;
  94.     HEADER *anhp = (HEADER *) answer;
  95.     u_char terrno = ETIMEDOUT;
  96. #define JUNK_SIZE 512
  97.     char junk[JUNK_SIZE]; /* buffer for trash data */
  98.  
  99. #ifdef RES_DEBUG
  100.         printf("res_send()\n");
  101.         __p_query(buf);
  102. #endif /* RES_DEBUG */
  103.  
  104.     v_circuit = (_res.options & RES_USEVC) || buflen > PACKETSZ;
  105.     id = hp->id;
  106.     /*
  107.      * Send request, RETRY times, or until successful
  108.      */
  109.     for (try = 0; try < _res.retry; try++) {
  110.       nscount = 0;
  111.       for (ns = (struct NameserventNode *)NDB->ndb_NameServers.mlh_Head;
  112.            ns->nsn_Node.mln_Succ;
  113.            ns = (struct NameserventNode *)ns->nsn_Node.mln_Succ) {
  114.         nscount++;
  115. #ifdef RES_DEBUG
  116.             printf("Querying server #%d address = %s\n", nscount,
  117.                   inet_ntoa(ns->nsn_Ent.ns_addr));
  118. #endif /* RES_DEBUG */
  119.         host.sin_len = sizeof(host);
  120.         host.sin_family = AF_INET;
  121.         host.sin_port = NAMESERVER_PORT;
  122.         host.sin_addr = ns->nsn_Ent.ns_addr;
  123.         aligned_bzero_const(&host.sin_zero, sizeof(host.sin_zero));
  124.     usevc:
  125.         if (v_circuit) {
  126.             int truncated = 0;
  127.  
  128.             /*
  129.              * Use virtual circuit;
  130.              * at most one attempt per server.
  131.              */
  132.             try = _res.retry;
  133.             if (res_sock < 0) {
  134.                 res_sock = socket(libPtr, AF_INET, SOCK_STREAM, 0);
  135.                 if (res_sock < 0) {
  136.                     terrno = readErrnoValue(libPtr);
  137. #ifdef RES_DEBUG
  138.                         perror("socket (vc) failed");
  139. #endif /* RES_DEBUG */
  140.                     continue;
  141.                 }
  142.                 if (connect(libPtr, res_sock,
  143.                         (struct sockaddr *)&host,
  144.                         sizeof(struct sockaddr)) < 0) {
  145.                         terrno = readErrnoValue(libPtr);
  146. #ifdef RES_DEBUG
  147.                         perror("connect failed");
  148. #endif /* RES_DEBUG */
  149.                     (void) CloseSocket(libPtr, res_sock);
  150.                     res_sock = -1;
  151.                     continue;
  152.                 }
  153.             }
  154.             /*
  155.              * Send length & message
  156.              */
  157.             len = htons((u_short)buflen);
  158.             if ((send(libPtr, res_sock, (char *)&len, sizeof(len), 0)
  159.                  != sizeof(len)) ||
  160.                ((send(libPtr, res_sock, (char *)buf, buflen, 0)
  161.                  != buflen))) {
  162.                 terrno = readErrnoValue(libPtr);
  163. #ifdef RES_DEBUG
  164.                     perror("write failed");
  165. #endif /* RES_DEBUG */
  166.                 (void) CloseSocket(libPtr, res_sock);
  167.                 res_sock = -1;
  168.                 continue;
  169.             }
  170.             /*
  171.              * Receive length & response
  172.              */
  173.             cp = answer;
  174.             len = sizeof(short);
  175.             while (len != 0 &&
  176.                 (n = recv(libPtr, res_sock,
  177.                       (char *)cp, (int)len, 0)) > 0) {
  178.                 cp += n;
  179.                 len -= n;
  180.             }
  181.             if (n <= 0) {
  182.                 terrno = readErrnoValue(libPtr);
  183. #ifdef RES_DEBUG
  184.                     perror("read failed");
  185. #endif /* RES_DEBUG */
  186.                 (void) CloseSocket(libPtr, res_sock);
  187.                 res_sock = -1;
  188.                 /*
  189.                  * A long running process might get its TCP
  190.                  * connection reset if the remote server was
  191.                  * restarted.  Requery the server instead of
  192.                  * trying a new one.  When there is only one
  193.                  * server, this means that a query might work
  194.                  * instead of failing.  We only allow one reset
  195.                  * per query to prevent looping.
  196.                  */
  197.                 if (terrno == ECONNRESET && !connreset) {
  198.                     connreset = 1;
  199.                     ns--;
  200.                 }
  201.                 continue;
  202.             }
  203.             cp = answer;
  204.             if ((resplen = ntohs(*(u_short *)cp)) > anslen) {
  205. #ifdef RES_DEBUG
  206.                            fprintf(stderr, "response truncated\n");
  207. #endif /* RES_DEBUG */
  208.                 len = anslen;
  209.                 truncated = 1;
  210.             } else
  211.                 len = resplen;
  212.             while (len != 0 &&
  213.                (n = recv(libPtr, res_sock,
  214.                      (char *)cp, (int)len, 0)) > 0) {
  215.                 cp += n;
  216.                 len -= n;
  217.             }
  218.             if (n <= 0) {
  219.                 terrno = readErrnoValue(libPtr);
  220. #ifdef RES_DEBUG
  221.                     perror("read failed");
  222. #endif /* RES_DEBUG */
  223.                 (void) CloseSocket(libPtr, res_sock);
  224.                 res_sock = -1;
  225.                 continue;
  226.             }
  227.             if (truncated) {
  228.                 /*
  229.                  * Flush rest of answer
  230.                  * so connection stays in synch.
  231.                  */
  232.                 anhp->tc = 1;
  233.                 len = resplen - anslen;
  234.                 while (len != 0) {
  235.                     n = (len > JUNK_SIZE ? JUNK_SIZE : len);
  236.                     if ((n = recv(libPtr, res_sock,
  237.                               junk, n, 0)) > 0)
  238.                         len -= n;
  239.                     else
  240.                         break;
  241.                 }
  242.             }
  243.         } else {
  244.             /*
  245.              * Use datagrams.
  246.              */
  247.             if (res_sock < 0) {
  248.                 res_sock = socket(libPtr, AF_INET, SOCK_DGRAM, 0);
  249.                 if (res_sock < 0) {
  250.                     terrno = readErrnoValue(libPtr);
  251. #ifdef RES_DEBUG
  252.                         perror("socket (dg) failed");
  253. #endif /* RES_DEBUG */
  254.                     continue;
  255.                 }
  256.             }
  257.             /*
  258.              * I'm tired of answering this question, so:
  259.              * On a 4.3BSD+ machine (client and server,
  260.              * actually), sending to a nameserver datagram
  261.              * port with no nameserver will cause an
  262.              * ICMP port unreachable message to be returned.
  263.              * If our datagram socket is "connected" to the
  264.              * server, we get an ECONNREFUSED error on the next
  265.              * socket operation, and select returns if the
  266.              * error message is received.  We can thus detect
  267.              * the absence of a nameserver without timing out.
  268.              * If we have sent queries to at least two servers,
  269.              * however, we don't want to remain connected,
  270.              * as we wish to receive answers from the first
  271.              * server to respond.
  272.              */
  273.             if (try == 0 && nscount == 1) {
  274.                 /*
  275.                  * Don't use connect if we might
  276.                  * still receive a response
  277.                  * from another server.
  278.                  */
  279.                 if (connected == 0) {
  280.                   if (connect(libPtr, res_sock,
  281.                           (struct sockaddr *)&host,
  282.                           sizeof(struct sockaddr)) < 0) {
  283. #ifdef RES_DEBUG
  284.                             perror("connect");
  285. #endif /* RES_DEBUG */
  286.                         continue;
  287.                     }
  288.                     connected = 1;
  289.                 }
  290.                 if (send(libPtr, res_sock,
  291.                      buf, buflen, 0) != buflen) {
  292. #ifdef RES_DEBUG
  293.                         perror("send");
  294. #endif /* RES_DEBUG */
  295.                     continue;
  296.                 }
  297.             } else {
  298.                 /*
  299.                  * Disconnect if we want to listen
  300.                  * for responses from more than one server.
  301.                  */
  302.                 if (connected) {
  303.                     (void) connect(libPtr, res_sock, &no_addr,
  304.                         sizeof(no_addr));
  305.                     connected = 0;
  306.                 }
  307.                 if (sendto(libPtr, res_sock, buf, buflen, 0,
  308.                     (struct sockaddr *)&host,
  309.                     sizeof(struct sockaddr)) != buflen) {
  310. #ifdef RES_DEBUG
  311.                         perror("sendto");
  312. #endif /* RES_DEBUG */
  313.                     continue;
  314.                 }
  315.             }
  316.  
  317.             /*
  318.              * Wait for reply
  319.              */
  320.             timeout.tv_sec = (_res.retrans << try);
  321.             if (try > 0)
  322.                 timeout.tv_sec /= nscount;
  323.             if (timeout.tv_sec <= 0)
  324.                 timeout.tv_sec = 1;
  325.             timeout.tv_usec = 0;
  326. wait:
  327.             FD_ZERO(&dsmask);
  328.             FD_SET(res_sock, &dsmask);
  329.             n = WaitSelect(libPtr, res_sock+1, &dsmask, NULL,
  330.                 NULL, &timeout, NULL);
  331.             if (n < 0) {
  332. #ifdef RES_DEBUG
  333.                     perror("select");
  334. #endif /* RES_DEBUG */
  335.                 continue;
  336.             }
  337.             if (n == 0) {
  338.                 /*
  339.                  * timeout
  340.                  */
  341. #ifdef RES_DEBUG
  342.                     printf("timeout\n");
  343. #endif /* RES_DEBUG */
  344. #if 1 || BSD >= 43
  345.                 gotsomewhere = 1;
  346. #endif
  347.                 continue;
  348.             }
  349.             if ((resplen = recv(libPtr, res_sock,
  350.                         answer, anslen, 0)) <= 0) {
  351. #ifdef RES_DEBUG
  352.                     perror("recvfrom");
  353. #endif /* RES_DEBUG */
  354.                 continue;
  355.             }
  356.             gotsomewhere = 1;
  357.             if (id != anhp->id) {
  358.                 /*
  359.                  * response from old query, ignore it
  360.                  */
  361. #ifdef RES_DEBUG
  362.                     printf("old answer:\n");
  363.                     __p_query(answer);
  364. #endif /* RES_DEBUG */
  365.                 goto wait;
  366.             }
  367.             if (!(_res.options & RES_IGNTC) && anhp->tc) {
  368.                 /*
  369.                  * get rest of answer;
  370.                  * use TCP with same server.
  371.                  */
  372. #ifdef RES_DEBUG
  373.                     printf("truncated answer\n");
  374. #endif /* RES_DEBUG */
  375.                 (void)CloseSocket(libPtr, res_sock);
  376.                 res_sock = -1;
  377.                 v_circuit = 1;
  378.                 goto usevc;
  379.             }
  380.         }
  381. #ifdef RES_DEBUG
  382.             printf("got answer:\n");
  383.             __p_query(answer);
  384. #endif /* RES_DEBUG */
  385.         /*
  386.          * If using virtual circuits, we assume that the first server
  387.          * is preferred * over the rest (i.e. it is on the local
  388.          * machine) and only keep that one open.
  389.          * If we have temporarily opened a virtual circuit,
  390.          * or if we haven't been asked to keep a socket open,
  391.          * close the socket.
  392.          */
  393.         if ((v_circuit &&
  394.             ((_res.options & RES_USEVC) == 0 || ns != 0)) ||
  395.             (_res.options & RES_STAYOPEN) == 0) {
  396.             (void) CloseSocket(libPtr, res_sock);
  397.             res_sock = -1;
  398.         }
  399.         return (resplen);
  400.        }
  401.     }
  402.     if (res_sock >= 0) {
  403.         (void) CloseSocket(libPtr, res_sock);
  404.         res_sock = -1;
  405.     }
  406.     if (v_circuit == 0)
  407.       if (gotsomewhere == 0)
  408.         writeErrnoValue(libPtr, ECONNREFUSED); /* no nameservers found */
  409.       else
  410.         writeErrnoValue(libPtr, ETIMEDOUT);       /* no answer obtained */
  411.     else
  412.       writeErrnoValue(libPtr, terrno);    
  413.     return (-1);
  414. }
  415.  
  416. /*
  417.  * This routine is for closing the socket if a virtual circuit is used and
  418.  * the program wants to close it.  This provides support for endhostent()
  419.  * which expects to close the socket.
  420.  *
  421.  * This routine is not expected to be user visible.
  422.  */
  423. void
  424. _res_close(struct SocketBase * libPtr)
  425. {
  426.     if (res_sock != -1) {
  427.         (void) CloseSocket(libPtr, res_sock);
  428.         res_sock = -1;
  429.     }
  430. }
  431.